package wortubung.util;

import java.util.HashMap;
import java.util.HashSet;

// File encoding: UTF-8
public class GreekScript {

	private static HashMap<Character,Transliterator.Consumer> toVernacularMap =
    		new HashMap<Character,Transliterator.Consumer>();
    private static HashSet<Character> vowelSet = new HashSet<Character>();
	private static HashMap<Character,String> toLatinMap = new HashMap<Character,String>();

    private static final String[] greekLetterInfo = {
	    "A    \u0391 Α   \u03B1 α    v",
	    "`A   \u1FBA Ὰ   \u1F70 ὰ    v",
	    "'A   \u1FBB Ά   \u1F71 ά    v",
	    "-A   \u1FB9 Ᾱ   \u1FB1 ᾱ    v",
	    "[A   \u1FB8 Ᾰ   \u1FB0 ᾰ    v",
	    "B    \u0392 Β   \u03B2 β",
	    "G    \u0393 Γ   \u03B3 γ",
	    "D    \u0394 Δ   \u03B4 δ",
	    "E    \u0395 Ε   \u03B5 ε    v",
	    "`E   \u1FC8 Ὲ   \u1F72 ὲ    v",
	    "'E   \u1FC9 Έ   \u1F73 έ    v",
	    "Z    \u0396 Ζ   \u03B6 ζ",
	    "`EH  \u1FCA Ὴ   \u1F74 ὴ    v",
	    "'EH  \u1FCB Ή   \u1F75 ή    v",
	    "TH   \u0398 Θ   \u03B8 θ",
	    "I    \u0399 Ι   \u03B9 ι    v",
	    "`I   \u1FDA Ὶ   \u1F76 ὶ    v",
	    "'I   \u1FDB Ί   \u1F77 ί    v",
	    "-I   \u1FD9 Ῑ   \u1FD1 ῑ    v",
	    "[I   \u1FD8 Ῐ   \u1FD0 ῐ    v",
	    ":I   \u03AA Ϊ   \u03CA ϊ    v",
	    "K    \u039A Κ   \u03BA κ",
	    "L    \u039B Λ   \u03BB λ",
	    "M    \u039C Μ   \u03BC μ",
	    "N    \u039D Ν   \u03BD ν",
	    "X    \u039E Ξ   \u03BE ξ",
	    "O    \u039F Ο   \u03BF ο    v",
	    "`O   \u1FF8 Ὸ   \u1F78 ὸ    v",
	    "'O   \u1FF9 Ό   \u1F79 ό    v",
	    "P    \u03A0 Π   \u03C0 π",
	    "R    \u03A1 Ρ   \u03C1 ρ",
	    "S    \u03A3 Σ   \u03C2 ς",
	    "S    \u03A3 Σ   \u03C3 σ",
	    "T    \u03A4 Τ   \u03C4 τ",
	    "U    \u03A5 Υ   \u03C5 υ    v",
	    "`U   \u1FEA Ὺ   \u1F7A ὺ    v",
	    "'U   \u1FEB Ύ   \u1F7B ύ    v",
	    "-U   \u1FE9 Ῡ   \u1FE1 ῡ    v",
	    "[U   \u1FE8 Ῠ   \u1FE0 ῠ    v",
	    ":U   \u03AB Ϋ   \u03CB ϋ    v",
	    "F    \u03A6 Φ   \u03C6 φ",
	    "CH   \u03A7 Χ   \u03C7 χ",
	    "PS   \u03A8 Ψ   \u03C8 ψ",
	    "OH   \u03A9 Ω   \u03C9 ω    v",
	    "`OH  \u1FFA Ὼ   \u1F7C ὼ    v",
	    "'OH  \u1FFB Ώ   \u1F7D ώ    v",
    };

    // :I   \u03AA Ϊ   \u03CA ϊ
    // :U   \u03AB Ϋ   \u03CB ϋ
    private static class ConsumerUmlaut extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) {
			if (consume(t, 'I', '\u03AA') ||
				consume(t, 'i', '\u03CA') ||
				consume(t, 'U', '\u03AB') ||
				consume(t, 'u', '\u03CB'));
		}
    }

    // Accent Acute:
	//    `A   \u1FBA Ὰ   \u1F70 ὰ
	//    `I   \u1FDA Ὶ   \u1F76 ὶ
    //    `U   \u1FEA Ὺ   \u1F7A ὺ
    //    `E   \u1FC8 Ὲ   \u1F72 ὲ
    //    `EH  \u1FCA Ὴ   \u1F74 ὴ
	//    `O   \u1FF8 Ὸ   \u1F78 ὸ
	//    `OH  \u1FFA Ὼ   \u1F7C ὼ
    // Accent Grave:
	//    'A   \u1FBB Ά   \u1F71 ά
    //    'I   \u1FDB Ί   \u1F77 ί
    //    'U   \u1FEB Ύ   \u1F7B ύ
	//    'E   \u1FC9 Έ   \u1F73 έ
	//    'EH  \u1FCB Ή   \u1F75 ή
    //    'O   \u1FF9 Ό   \u1F79 ό
    //    'OH  \u1FFB Ώ   \u1F7D ώ
    private static class ConsumerAccent extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			boolean isAcute = charAt(t) == '\'';
			if (consume(t, 'A', isAcute ? '\u1FBB' : '\u1FBA') ||	// 'A `A
				consume(t, 'a', isAcute ? '\u1F71' : '\u1F70') ||	// 'a `a
				consume(t, 'I', isAcute ? '\u1FDB' : '\u1FDA') ||	// 'I `I
				consume(t, 'i', isAcute ? '\u1F77' : '\u1F76') ||	// 'i `i
				consume(t, 'U', isAcute ? '\u1FEB' : '\u1FEA') ||	// 'U `U
				consume(t, 'u', isAcute ? '\u1F7B' : '\u1F7A'))		// 'u `u
				return;

			forward(t, 1);
			switch (charAt(t)) {
			case 'E':	if (!consume(t, 'H', isAcute ? '\u1FCB' : '\u1FCA'))	// 'EH	`EH
							consume(t,       isAcute ? '\u1FC9' : '\u1FC8');	// 'E	`E
						break;
			case 'e':	if (!consume(t, 'h', isAcute ? '\u1F75' : '\u1F74'))	// 'eh	`eh
							consume(t,       isAcute ? '\u1F73' : '\u1F72');	// 'e	`e
						break;
			case 'O':	if (!consume(t, 'H', isAcute ? '\u1FFB' : '\u1FFA'))	// 'OH	`OH
							consume(t,       isAcute ? '\u1FF9' : '\u1FF8');	// 'O	`O
						break;
			case 'o':	if (!consume(t, 'h', isAcute ? '\u1F7D' : '\u1F7C'))	// 'oh	`oh
							consume(t,       isAcute ? '\u1f79' : '\u1F78');	// 'o	`o
						break;
			default:	invalidChar(t);
			}
		}
    }

    // Macron:
    //    -A   \u1FB9 Ᾱ   \u1FB1 ᾱ
    //    -I   \u1FD9 Ῑ   \u1FD1 ῑ
    //    -U   \u1FE9 Ῡ   \u1FE1 ῡ
    // Breve:
    //    [A   \u1FB8 Ᾰ   \u1FB0 ᾰ
	//    [I   \u1FD8 Ῐ   \u1FD0 ῐ
	//    [U   \u1FE8 Ῠ   \u1FE0 ῠ
    private static class ConsumerMacronBreve extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			boolean isMacron = charAt(t) == '-';
			if (consume(t, 'A', isMacron ? '\u1FB9' : '\u1FB8') ||	// -A	[A
				consume(t, 'a', isMacron ? '\u1FB1' : '\u1FB0') ||	// -a	[a
				consume(t, 'I', isMacron ? '\u1FD9' : '\u1FD8') ||	// -I	[I
				consume(t, 'i', isMacron ? '\u1FD1' : '\u1FD0') ||	// -i	[i
				consume(t, 'U', isMacron ? '\u1FE9' : '\u1FE8') ||	// -U	[U
				consume(t, 'u', isMacron ? '\u1FE1' : '\u1FE0'))	// -u	[u
				return;
			invalidChar(t);
		}
    }

    //	CH   \u03A7 Χ   \u03C7 χ
    private static class ConsumerC extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			if (charAt(t) == 'c' && consume(t, 'h', '\u03C7') ||	// ch
								    consume(t, 'H', '\u03A7'))		// CH
				return;
			invalidChar(t);
		}
    }

	//  E    \u0395 Ε   \u03B5 ε
	//  EH   \u0397 Η   \u03B7 η
    private static class ConsumerE extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			if (charAt(t) == 'e') { // lower case
				if (!consume(t, 'h', '\u03B7'))		// eh
					consume(t, '\u03B5');			// e
			} else {
				if (!consume(t, 'H', '\u0397'))		// EH
					consume(t, '\u0395');			// E
			}
		}
    }

    //  O    \u039F Ο   \u03BF ο
	//  OH   \u03A9 Ω   \u03C9 ω
    private static class ConsumerO extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			if (charAt(t) == 'o') { // lower case
				if (!consume(t, 'h', '\u03C9'))		// oh
					consume(t, '\u03BF');			// o
			} else {
				if (!consume(t, 'H', '\u03A9'))		// OH
					consume(t, '\u039F');			// O
			}
		}
    }

	//  P    \u03A0 Π   \u03C0 π
	//  PS   \u03A8 Ψ   \u03C8 ψ
    private static class ConsumerP extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			if (charAt(t) == 'p') { // lower case
				if (!consume(t, 's', '\u03C8'))		// ps
					consume(t, '\u03C0');			// p
			} else {
				if (!consume(t, 'S', '\u03A8'))		// PS
					consume(t, '\u03A0');			// P
			}
		}
    }

	//  S    \u03A3 Σ   \u03C3 σ
	//  s               \u03C2 ς (when 's' is at the word end)
    private static class ConsumerS extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			if (charAt(t) == 'S') // upper case
				consume(t, '\u03A3');
			else if (isAtWordEnd(t))
				consume(t, '\u03C2');
			else
				consume(t, '\u03C3');
		}
    }


	//  T    \u03A4 Τ   \u03C4 τ
	//  TH   \u0398 Θ   \u03B8 θ
    private static class ConsumerT extends Transliterator.Consumer {
		@Override public void proceed(Transliterator t) throws TransliterateException {
			if (charAt(t) == 't') { // lower case
				if (!consume(t, 'h', '\u03B8'))		// th
					consume(t, '\u03C4');			// t
			} else {
				if (!consume(t, 'H', '\u0398'))		// TH
					consume(t, '\u03A4');			// T
			}
		}
    }

    static {
        toVernacularMap.put(':', new ConsumerUmlaut());
        toVernacularMap.put('`', new ConsumerAccent());			toVernacularMap.put('\'',toVernacularMap.get('`'));
        toVernacularMap.put('-', new ConsumerMacronBreve());	toVernacularMap.put('[', toVernacularMap.get('-'));

    	toVernacularMap.put('C', new ConsumerC()); 	toVernacularMap.put('c', toVernacularMap.get('C'));
    	toVernacularMap.put('E', new ConsumerE()); 	toVernacularMap.put('e', toVernacularMap.get('E'));
    	toVernacularMap.put('O', new ConsumerO()); 	toVernacularMap.put('o', toVernacularMap.get('O'));
    	toVernacularMap.put('P', new ConsumerP()); 	toVernacularMap.put('p', toVernacularMap.get('P'));
    	toVernacularMap.put('S', new ConsumerS()); 	toVernacularMap.put('s', toVernacularMap.get('S'));
    	toVernacularMap.put('T', new ConsumerT()); 	toVernacularMap.put('t', toVernacularMap.get('T'));

    	for (String infoString : greekLetterInfo) {
			String[] sa = infoString.split(" +");
			String latin = sa[0];
			char upperCase = sa[1].charAt(0);
			char lowerCase = sa[3].charAt(0);
			if (sa.length == 5) { // is a vowel
				vowelSet.add(upperCase);
				vowelSet.add(lowerCase);
			}

			char c = latin.charAt(0);
			if (latin.length() == 1 && !toVernacularMap.containsKey(c)) {
				toVernacularMap.put(c, new Transliterator.OneToOneConsumer(upperCase));
				c = Character.toLowerCase(c);
				toVernacularMap.put(c, new Transliterator.OneToOneConsumer(lowerCase));
			}
    	}
	}

    public static void toGreek(String[] sa) throws TransliterateException {
    	if (sa != null) {
	    	for (int i=sa.length-1; i>=0; --i)
	    		sa[i] = toGreek(sa[i]);
    	}
    }

    public static String toGreek(String latinInput) throws TransliterateException {
    	return new Transliterator(toVernacularMap).getVernacular(latinInput);
    }

    public static void toLatin(String[] sa) {
    	toLatin(sa, false);
    }

    public static String toLatin(char cyrillicInput, boolean withDiacritics) {
		String s = toLatinMap.get(cyrillicInput);
		if (!withDiacritics && !Character.isAlphabetic(s.charAt(0)))
			s = s.substring(1);
		return s;
    }

    public static void toLatin(String[] sa, boolean withDiacritics) {
    	if (sa == null)
    		return;
    	for (int i=sa.length-1; i>=0; --i)
    		sa[i] = toLatin(sa[i], withDiacritics);
    }

    public static String toLatin(String cyrillicInput, boolean withDiacritics) {
    	StringBuilder sb = new StringBuilder();
    	for (int i=0; i<cyrillicInput.length(); ++i)
    		sb.append(toLatin(cyrillicInput.charAt(i), withDiacritics));
    	return sb.toString();
    }

    public static boolean startsWithVowel(String str) {
    	return str.length() > 0 && isVowel(str.charAt(0));
    }

    public static boolean endsWithVowel(String str) {
    	return str.length() > 0 && isVowel(str.charAt(str.length()-1));
    }

    public static boolean isVowel(char ch) {
        return vowelSet.contains(ch);
    }

    public static void main(String[] args) throws Exception {
    	ToolBase.createAndShowGUI();
    	ToolBase output = new ToolBase();

    	String s = toGreek(args[0]);
    	output.printLine(s);

//    	String fmt = "%-3s [%s] %4s [%s] %4s    [%s] %4s [%s] %4s\r\n";
//    	for (String s : greekLetterInfo) {
//    		String[] sa = s.split(" +");
//    		ToolBase.out.print(String.format(fmt, sa[0],
//    				sa[1], Integer.toHexString((int)sa[1].charAt(0)),
//    				sa[2], Integer.toHexString((int)sa[2].charAt(0)),
//    				sa[3], Integer.toHexString((int)sa[3].charAt(0)),
//    				sa[4], Integer.toHexString((int)sa[4].charAt(0))));
//    	}
    }
}
